package cn.good.yan.controller;

import cn.good.yan.service.RateBlockHandler;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * 演示：@SentinelResource注解
 *
 * @author shengren.yan
 * @create 2024-05-25
 */
@RestController
@Slf4j
public class Rate2Controller {

    // @SentinelResource 注解默认不使用，使用url进行配置，绑定
    @GetMapping("/r1/test1")
    public String r1() {
        return "按rest地址限流测试";
    }

    // 演示1：当我们配置流控，资源名: byResour1 ，阈值类型：QPS , 阈值：2 ，流控效果：快速失败，流控模式：直连
    // 访问：http://localhost:8401/r1/test2  单次访问正常，
    // 访问：http://localhost:8401/r1/test2  多次访问，触发流控，执行handlerTest（）方法逻辑
    @GetMapping("/r1/test2")
    @SentinelResource(value = "byResour1", blockHandler = "handlerTest")
    public String test2() {
        return "按照资源名称byResour1 - 进行限流测试--------";
    }

    // 自定义 - 服务降级后的处理 - 方法
    public String handlerTest(BlockException blockException) {
        return "服务不可用，触发了自定义，流控的处理";
    }


    // 演示2：当我们配置流控，资源名: byResour2 ，阈值类型：QPS , 阈值：2 ，流控效果：快速失败，流控模式：直连
    // 访问：http://localhost:8401/r1/test3/1 单次访问正常，
    // 访问：http://localhost:8401/r1/test3/1 多次访问，触发流控，执行 handlerTest2（）方法逻辑，
    // 访问：http://localhost:8401/r1/test3/0 单次访问，触发异常，执行 fallback2（）方法逻辑，
    // 访问：http://localhost:8401/r1/test3/0 多次访问，触发流控，执行 handlerTest2（）方法逻辑，多次访问异常，也会执行流控的逻辑方法
    @GetMapping("/r1/test3/{id}")
    @SentinelResource(value = "byResour2", blockHandler = "handlerTest2", fallback = "fallback2")
    public String test3(@PathVariable("id") Integer id) {
        if (id == 0) {
            throw new RuntimeException("当值为0，触发异常");
        }
        return "访问正常ok";
    }

    // 自定义 - 服务降级后的处理 - 方法
    public String handlerTest2(@PathVariable("id") Integer id, BlockException e) {
        log.error("sentinel配置自定义限流了", e);
        return "sentinel配置自定义限流了";
    }

    // 自定义 - 服务异常后的处理 - 方法
    public String fallback2(@PathVariable("p1") Integer p1, Throwable e) {
        log.error("程序逻辑异常了:{}", e);
        return "程序逻辑异常，执行" + "\t" + e.getMessage();
    }

    // 演示3：当我们配置流控，资源名: byResour2 ，阈值类型：QPS , 阈值：2 ，流控效果：快速失败，流控模式：直连
    // 效果与 test3方式一样，只是触发流控，执行 blockHandlerClass类，里的 test4 方法
    @GetMapping("/r1/test4/{id}")
    @SentinelResource(value = "byResour2",
            blockHandlerClass = RateBlockHandler.class,
            blockHandler = "test4",
            fallback = "fallback2")
    public String test4(@PathVariable("id") Integer id) {
        if (id == 0) {
            throw new RuntimeException("当值为0，触发异常");
        }
        return "访问正常ok";
    }


    // 演示4：热点参数限制 - 当我们配置热点，资源名: byResour3 ，参数索引：0 （代表参数1 p1），单机阈值：2
    // 访问：http://localhost:8401/r1/test5  单次访问正常，不会触发流控
    // 访问：http://localhost:8401/r1/test5?p1=1 多次访问，会触发流控，进入 testHotKey 方法
    // 访问：http://localhost:8401/r1/test5?p1=1&p2=22 多次访问，会触发流控，进入 testHotKey 方法
    // 访问：http://localhost:8401/r1/test5?p2=22 多次访问，不会触发流控
    // 演示5：热点参数限制 - 当我们配置热点，同上，但在参数列外项，选择对应的参数类型，参数值是：某个值，限制阈值：20
    // 访问：http://localhost:8401/r1/test5?p1=所设置的某个值 多次访问，需要达到-限制阈值。才会触发流控，进入 testHotKey 方法
    // 做到参数，对某个参数值，达到具体的流控
    @GetMapping("/r1/test5")
    @SentinelResource(value = "byResour3", blockHandler = "testHotKey")
    public String test5(@RequestParam(value = "p1", required = false) String p1,
                        @RequestParam(value = "p2", required = false) String p2) {
        return "------test热点限制ok";
    }

    public String testHotKey(String p1, String p2, BlockException exception) {
        return "-----自定义-热点流控触发";
    }


    // 演示4：授权 - 达到通过接口来源判断是否允许执行本次请求
    // 重写 MyRequestOriginParser接口
    // 配置：资源名：/r1/test6 ， 流控应用：t1,t2（放行的参数值）,授权类型: 白名单/黑名单 （案例选择 黑名单）
    // 访问：http://localhost:8401/r1/test6  多次访问正常，不会限制
    // 访问：http://localhost:8401/r1/test6?serverName=t1  会被授权，限制
    // 访问：http://localhost:8401/r1/test6?serverName=t2  会被授权，限制
    // 访问：http://localhost:8401/r1/test6?serverName=t22  不会限制
    @GetMapping("/r1/test6")
    public String test6() {
        log.info("测试--授权规则test6");
        return "Sentinel授权规则演示";
    }

    // 演示5：持久化配置 ， 在我们重启后，页面上配置都会有清空，
    // 我们进行持久化配置，步骤1：引入持久化包、步骤2： 在yml里配置对应的配置
    // 我们以 /r1/test2 接口为例：
    // nacos 上 添加对应的 配置文件，是json格式
//    [
//        {
//            "resource":"byResour1",
//                "limitApp":"default",
//                "grade":1,
//                "count":1,
//                "strategy":0,
//                "controlBehavior":0,
//                "clusterMode":false,
//                "warmUpPeriodSec": 10,
//                "maxQueueingTimeMs": 500,
//                "refResource": "rrr"
//        }
//    ]
    // 配置完，我们重启
    // 访问：http://localhost:8401/r1/test2  单次访问 正常
    // 访问：http://localhost:8401/r1/test2  多次访问 进入熔流控


}
